package com.mioto.ld.module.alert.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mioto.ld.communication.mqtt.MqttHelper;
import com.mioto.ld.communication.mqtt.protocol.model.server.TimedTaskDelPayload;
import com.mioto.ld.communication.mqtt.protocol.model.server.TimedTaskSetPayload;
import com.mioto.ld.component.BasePager;
import com.mioto.ld.component.EditType;
import com.mioto.ld.module.alert.model.*;
import com.mioto.ld.module.alert.service.ILinkageFunctionService;
import com.mioto.ld.module.alert.service.ILinkageTriggerService;
import com.mioto.ld.module.device.service.IDeviceService;
import com.mioto.ld.utils.BaseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import com.mioto.ld.module.alert.dao.LinkageDao;
import com.mioto.ld.module.alert.service.ILinkageService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 *
 * @author mioto
 * @date 2024-05-20 23:49:40
 */
@Service("linkageService")
@Slf4j
public class LinkageServiceImpl implements ILinkageService{

    @Resource
    private LinkageDao linkageDao;
    @Resource
    private ILinkageTriggerService linkageTriggerService;
    @Resource
    private ILinkageFunctionService linkageFunctionService;
    @Resource
    private IDeviceService deviceService;

    @Override
    public List<Linkage> findList(Linkage linkage) {
        return linkageDao.findList(linkage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insert(LinkageDTO linkage) {
        linkage.setStatus(AlertStatus.ALERT_STATUS_ENABLE);
        linkage.initCreateData();
        int result = linkageDao.insertIgnoreNull(linkage);
        //添加触发器
        if (CollUtil.isNotEmpty(linkage.getLinkageTriggerDTOS())){
            linkage.getLinkageTriggerDTOS().forEach(linkageTriggerDTO -> {
                linkageTriggerDTO.setLinkageId(linkage.getId());
                linkageTriggerDTO.setTriggerCondition(linkageTriggerDTO.toJsonStr());
                linkageTriggerService.insert(linkageTriggerDTO);
            });
            //添加执行动作
            if (CollUtil.isNotEmpty(linkage.getLinkageFunctionDTOS())){
                linkage.getLinkageFunctionDTOS().forEach(linkageFunctionDTO -> {
                    linkageFunctionDTO.setLinkageId(linkage.getId());
                    linkageFunctionService.insert(linkageFunctionDTO);
                });
            }
        }
        //向设备发送消息
        addTimedTask(linkage);
        return result;
    }

    @Override
    public int update(Linkage linkage) {
        return linkageDao.update(linkage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateIgnoreNull(LinkageDTO linkage) {
        if (ObjectUtil.isNotNull(linkage.getId())){
            linkageDao.updateIgnoreNull(linkage);
        }
        if (CollUtil.isNotEmpty(linkage.getLinkageTriggerDTOS())){
            Linkage link;
            LinkageTrigger linkageTrigger;
            int[] linkageTriggerIds;
            for (LinkageTriggerDTO linkageTriggerDTO : linkage.getLinkageTriggerDTOS()){
                switch (linkageTriggerDTO.getEditType()){
                    case EditType.EDIT_TYPE_DELETE:
                        //是否为定时任务，如果是同步设备删除改任务
                        linkageTrigger = linkageTriggerService.findByColumn("id",linkageTriggerDTO.getId());
                        if (ObjectUtil.equal(linkageTrigger.getTriggerTarget(),TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED)){
                            link = findByColumn("id",linkageTrigger.getLinkageId());
                            linkageTriggerIds = linkageTriggerService.findIdsByLinkageId(linkageTrigger.getLinkageId(),TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED);
                            deleteTimedTask(link,linkageTriggerIds);
                        }
                        //删除数据库记录
                        linkageTriggerService.deleteByColumn("id",linkageTriggerDTO.getId());
                        break;
                    case EditType.EDIT_TYPE_INSERT:
                        linkageTriggerDTO.setTriggerCondition(linkageTriggerDTO.toJsonStr());
                        linkageTriggerService.insert(linkageTriggerDTO);
                        //如果是添加定时任务，向设备同步消息
                        if(ObjectUtil.equal(linkageTriggerDTO.getTriggerTarget(),TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED)){
                            link = findByColumn("id",linkageTriggerDTO.getLinkageId());
                            sendTimedTaskMessage(linkageTriggerDTO,findDeviceSns(link));
                        }
                        break;
                    case EditType.EDIT_TYPE_UPDATE:
                        linkageTriggerDTO.setTriggerCondition(linkageTriggerDTO.toJsonStr());
                        linkageTriggerService.updateIgnoreNull(linkageTriggerDTO);
                        //是否为定时任务，如果是先同步设备删除改任务，再添加
                        linkageTrigger = linkageTriggerService.findByColumn("id", linkageTriggerDTO.getId());
                        if (ObjectUtil.equal(linkageTrigger.getTriggerTarget(), TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED)) {
                            link = findByColumn("id", linkageTrigger.getLinkageId());
                            deleteTimedTask(link, new int[]{linkageTrigger.getId()});
                            sendTimedTaskMessage(linkageTriggerDTO, findDeviceSns(link));
                        }
                        break;
                }
            }
        }

        if (CollUtil.isNotEmpty(linkage.getLinkageFunctionDTOS())){
            linkage.getLinkageFunctionDTOS().forEach(linkageFunctionDTO -> {
                switch (linkageFunctionDTO.getEditType()){
                    case EditType.EDIT_TYPE_DELETE:
                        linkageFunctionService.deleteByColumn("id",linkageFunctionDTO.getId());
                        break;
                    case EditType.EDIT_TYPE_INSERT:
                        linkageFunctionService.insert(linkageFunctionDTO);
                        break;
                    case EditType.EDIT_TYPE_UPDATE:
                        linkageFunctionService.updateIgnoreNull(linkageFunctionDTO);
                        break;
                }
            });
        }
        return 1;
    }

    @Override
    public Linkage findByColumn(String column, Object value) {
        return linkageDao.findByColumn(column,value);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByColumn(String column, Object value) {
        int linkageId = Integer.parseInt(value.toString());
        Linkage linkage = findByColumn("id",linkageId);
        int[] ids = linkageTriggerService.findIdsByLinkageId(linkageId,TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED);
        int result = linkageDao.deleteByColumn(column,value);
        linkageTriggerService.deleteByColumn("linkage_id",linkageId);
        linkageFunctionService.deleteByColumn("linkage_id",linkageId);
        deleteTimedTask(linkage,ids);
        return result;
    }

    @Override
    public int batchDelete(Integer[] ids) {
        return linkageDao.batchDelete(ids);
    }

    @Override
    public List<LinkagePagerVO> findPager(Linkage linkage, BasePager basePager) {
        int pageNo = 0;
        if (basePager.getPage() > 1){
            pageNo = (basePager.getPage() - 1)*basePager.getRows();
        }
        return linkageDao.pager(linkage,pageNo,basePager.getRows(), BaseUtil.buildUserFilterCondition());
    }

    @Override
    public LinkageStatusStatisticVO findCount(Linkage linkage) {
        LinkageStatusStatisticVO statisticVO = linkageDao.findCount(linkage,BaseUtil.buildUserFilterCondition());
        if (statisticVO.getTotalCount() > 0){
            statisticVO.setDisableCount(statisticVO.getTotalCount() - statisticVO.getEnableCount());
        }
        return statisticVO;
    }

    @Override
    public LinkageDetailVO findDetail(int id) {
        return linkageDao.findDetail(id);
    }

    private boolean isTimedTask(List<LinkageTriggerDTO> linkageTriggerDTOs){
        for (LinkageTriggerDTO linkageTriggerDTO : linkageTriggerDTOs) {
            if (ObjectUtil.equal(linkageTriggerDTO.getTriggerTarget(), TriggerTargetType.TRIGGER_TARGET_TYPE_TIMED)){
                return true;
            }
            return false;
        }
        return false;
    }

    private void addTimedTask(LinkageDTO linkage){
        if (CollUtil.isNotEmpty(linkage.getLinkageTriggerDTOS()) && isTimedTask(linkage.getLinkageTriggerDTOS())){
            String[] deviceSns = findDeviceSns(linkage);
            for (LinkageTriggerDTO linkageTriggerDTO : linkage.getLinkageTriggerDTOS()) {
                sendTimedTaskMessage(linkageTriggerDTO,deviceSns);
            }
        }
    }

    private void sendTimedTaskMessage(LinkageTriggerDTO linkageTriggerDTO,String[] deviceSns){
        TimedTaskSetPayload timedTaskSetPayload = new TimedTaskSetPayload();
        timedTaskSetPayload.setNo(linkageTriggerDTO.getId().toString());
        timedTaskSetPayload.setTime( DateUtil.parse(linkageTriggerDTO.getTriggerConditionDTO().getTime(),"HH:mm").getTime());
        timedTaskSetPayload.setRepeatRule(StrUtil.splitToInt(linkageTriggerDTO.getTriggerConditionDTO().getRepeatRule(),StrUtil.COMMA));
        for (String deviceSn : deviceSns) {
            MqttHelper.timedTaskSet(deviceSn,timedTaskSetPayload);
        }
    }

    private void deleteTimedTask(Linkage linkage,int[] linkageTriggerIds){
        if (ArrayUtil.isNotEmpty(linkageTriggerIds)){
            String[] deviceSns = findDeviceSns(linkage);
            TimedTaskDelPayload payload;
            for (int linkageTriggerId : linkageTriggerIds) {
                payload = new TimedTaskDelPayload();
                payload.setNo(String.valueOf(linkageTriggerId));
                for (String deviceSn : deviceSns) {
                    MqttHelper.timedTaskSet(deviceSn,payload);
                }
            }
        }
    }

    private String[] findDeviceSns(Linkage linkage){
        String[] deviceSns = new String[0];
        if (ObjectUtil.equal(linkage.getRelationType(), RelationType.RELATION_TYPE_PRODUCT)){
            deviceSns = deviceService.findSnsByProductId(linkage.getProductId());
        }else if (ObjectUtil.equal(linkage.getRelationType(), RelationType.RELATION_TYPE_DEVICE)){
            deviceSns = deviceService.findSnsByDeviceIds(linkage.getDeviceIds());
        }
        return deviceSns;
    }
}